use std::time::{SystemTime, UNIX_EPOCH};

// 闰年的毫秒数
const LEAP_YEAR_MS: u64 = 366 * 24 * 60 * 60 * 1000;
// 普通年的毫秒数
const YEAR_MS: u64 = 365 * 24 * 60 * 60 * 1000;
// 大月毫秒数，31天
const BIG_MONTH_MS: u64 = 31 * 24 * 60 * 60 * 1000;
// 小月毫秒数，30天
const SMALL_MONTH_MS: u64 = 30 * 24 * 60 * 60 * 1000;
// 闰年2月毫秒数
const LEAP_FEB_MS: u64 = 29 * 24 * 60 * 60 * 1000;
// 普通年2月毫秒数
const FEB_MS: u64 = 28 * 24 * 60 * 60 * 1000;
// 1天的毫秒数
const DAY_MS: u64 = 24 * 60 * 60 * 1000;
// 1小时的毫秒数
const HOUR_MS: u64 = 60 * 60 * 1000;
// 1分钟的毫秒数
const MINUTE_MS: u64 = 60 * 1000;
// 1秒的毫秒数
const SECOND_MS: u64 = 1000;
// unix时间戳开始年份：1970-01-01 08:00
const START_YEAR: u64 = 1970;
// unix时间戳开始小时：1970-01-01 08:00
const START_HOUR: u8 = 8;
// 1970-01-01 08:00 星期几
const START_DAY_OF_WEEK: u8 = 4;
// 星期几
// const DAY_OF_WEEK: Vec<&str> = vec!["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const SUN: &str = "Sun";
const MON: &str = "Mon";
const TUE: &str = "Tue";
const WED: &str = "Wed";
const THU: &str = "Thu";
const FRI: &str = "Fri";
const SAT: &str = "Sat";

pub struct DateTime {
    timestamp: u64,
    date: Date,
    time: Time,
    day_vec: Vec<String>,
    update_time: u64,
}

struct Date {
    year: u64,
    month: u8,
    day: u8,
    day_of_week: usize,
}

struct Time {
    hour: u8,
    minute: u8,
    second: u8,
}

impl DateTime {
    pub fn new() -> Self {
        let ts = DateTime::timestamp();
        DateTime {
            timestamp: ts,
            date: Date::new(ts),
            time: Time::new(ts),
            day_vec: vec![
                String::from(SUN),
                String::from(MON),
                String::from(TUE),
                String::from(WED),
                String::from(THU),
                String::from(FRI),
                String::from(SAT),
            ],
            update_time: ts,
        }
    }

    pub fn display(&mut self) -> String {
        self.update();
        format!(
            "{:02}-{:02} {} {:02}:{:02}",
            self.date.month,
            self.date.day,
            self.day_vec[self.date.day_of_week],
            self.time.hour,
            self.time.minute
        )
    }

    fn update(&mut self) {
        let ts = DateTime::timestamp();
        self.timestamp = ts;
        if ts - self.update_time >= 60000 {
            self.update_time = ts;
            self.date.update(ts);
            self.time.update(ts);
        }
    }

    // 获取系统时间戳
    pub fn timestamp() -> u64 {
        let time = SystemTime::now();
        let duration = time
            .duration_since(UNIX_EPOCH)
            .expect("Time went backwards");
        // duration.as_secs() * 1000 + (duration.subsec_nanos() as f64 / 1_000_000.0) as u64
        duration.as_millis() as u64
    }
}

impl Date {
    fn new(timestamp: u64) -> Self {
        let (year, month, day, day_of_week) = Date::parse_date(timestamp);
        Date {
            year,
            month,
            day,
            day_of_week,
        }
    }

    fn update(&mut self, timestamp: u64) {
        let (year, month, day, day_of_week) = Date::parse_date(timestamp);
        self.year = year;
        self.month = month;
        self.day = day;
        self.day_of_week = day_of_week;
    }

    // 通过时间戳解析日期
    fn parse_date(timestamp: u64) -> (u64, u8, u8, usize) {
        let mut ts = timestamp;
        let day_of_week = (((ts + START_HOUR as u64 * HOUR_MS) / DAY_MS % 7
            + START_DAY_OF_WEEK as u64)
            % 7) as usize;
        let mut year: u64 = START_YEAR;
        let mut month: u8 = 1;
        let mut day: u8 = 1;
        while ts > YEAR_MS {
            let is_leap = Date::is_leap_year(year);
            if is_leap {
                if ts < LEAP_YEAR_MS {
                    break;
                }
                ts -= LEAP_YEAR_MS;
            } else {
                ts -= YEAR_MS;
            }
            year += 1;
        }

        while ts > FEB_MS {
            if month == 1
                || month == 3
                || month == 5
                || month == 7
                || month == 8
                || month == 10
                || month == 12
            {
                if ts < BIG_MONTH_MS {
                    break;
                }
                ts -= BIG_MONTH_MS;
            } else if month == 4 || month == 6 || month == 9 || month == 11 {
                if ts < SMALL_MONTH_MS {
                    break;
                }
                ts -= SMALL_MONTH_MS;
            } else {
                let is_leap = Date::is_leap_year(year);
                if is_leap {
                    if ts < LEAP_FEB_MS {
                        break;
                    }
                    ts -= LEAP_FEB_MS;
                } else {
                    ts -= FEB_MS;
                }
            }
            if month == 12 {
                month = 1;
            } else {
                month += 1;
            }
        }

        day += (ts / DAY_MS) as u8;

        (year, month, day, day_of_week)
    }

    fn is_leap_year(year: u64) -> bool {
        if year % 100 == 0 {
            year % 400 == 0
        } else {
            year % 4 == 0
        }
    }
}

impl Time {
    fn new(timestamp: u64) -> Self {
        let (hour, minute, second) = Time::parse_time(timestamp);
        Time {
            hour,
            minute,
            second,
        }
    }

    fn update(&mut self, timestamp: u64) {
        let (hour, minute, second) = Time::parse_time(timestamp);
        self.hour = hour;
        self.minute = minute;
        self.second = second;
    }

    fn parse_time(timestamp: u64) -> (u8, u8, u8) {
        let mut ts = timestamp;
        ts %= DAY_MS;
        let mut hour = (ts / HOUR_MS) as u8 + START_HOUR;
        if hour >= 24 {
            hour -= 24;
        }
        ts %= HOUR_MS;
        let minute = (ts / MINUTE_MS) as u8;
        ts %= MINUTE_MS;
        let second = (ts / SECOND_MS) as u8;
        (hour, minute, second)
    }
}
